package com.jnc.pay.biz.common.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.jnc.pay.biz.common.mapper.ContractMapper;
import com.jnc.pay.biz.common.mapper.RefundMapper;
import com.jnc.pay.biz.common.mapper.TradeMapper;
import com.jnc.pay.biz.common.mapper.TransMapper;
import com.jnc.pay.biz.common.model.Contract;
import com.jnc.pay.biz.common.model.Refund;
import com.jnc.pay.biz.common.model.Trade;
import com.jnc.pay.biz.common.model.Trans;
import com.jnc.pay.biz.common.service.CommonService;
import com.jnc.pay.biz.common.vo.PayReq;
import com.jnc.pay.biz.common.vo.RefundReq;
import com.jnc.pay.constant.BizConstant;
import com.jnc.pay.core.config.redis.lock.DistributedLock;
import com.jnc.pay.core.exception.IdempotencyException;
import com.jnc.pay.core.exception.LockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * @Auther: jjn
 * @Date: 2020/7/29
 * @Desc:
 */
@Slf4j
@Service
public class CommonServiceImpl implements CommonService {

    public static final String PAY = "pay";
    public static final String REFUND = "refund";
    public static final String TRANS = "trans";
    public static final String CONTRACT = "contract";

    @Resource
    TradeMapper tradeMapper;
    @Resource
    RefundMapper refundMapper;
    @Resource
    TransMapper transMapper;
    @Resource
    ContractMapper contractMapper;
    @Autowired
    DistributedLock locker;

    @Override
    public void addTrade(Trade model, DistributedLock locker, String lockKey, TradeMapper tradeMapper) {
        if (locker == null) {
            throw new LockException("DistributedLock is null");
        }
        if (StrUtil.isEmpty(lockKey)) {
            throw new LockException("lockKey is null");
        }
        Object lock = null;
        try {
            //加锁
            try {
                lock = locker.tryLock(lockKey, 10, 60, TimeUnit.SECONDS);
            } catch (Exception e) {
                log.error("get lock exception, req: {}, error: {}", model, e);
                throw new LockException("获取锁异常");
            }
            if (lock != null) {
                //判断记录是否已存在
                Integer count = tradeMapper.queryCount(model);
                if (count == null || count < 1) {
                    tradeMapper.addTrade(model);
                } else {
                    String msg = "appId[" + model.getAppId() + "]，已存在支付订单[" + model.getAppOrderId() + "]";
                    throw new IdempotencyException(msg);
                }
            } else {
                throw new LockException("锁等待超时");
            }
        } finally {
            try {
                locker.unlock(lock);
            } catch (Exception e) {
                log.error("Lock release exception: {}", e);
                throw new LockException("释放锁异常");
            }
        }
    }

    @Override
    public void addRefund(Refund model, DistributedLock locker, String lockKey, RefundMapper refundMapper) {
        if (locker == null) {
            throw new LockException("DistributedLock is null");
        }
        if (StrUtil.isEmpty(lockKey)) {
            throw new LockException("lockKey is null");
        }
        Object lock = null;
        try {
            //加锁
            try {
                lock = locker.tryLock(lockKey, 10, 60, TimeUnit.SECONDS);
            } catch (Exception e) {
                log.error("get lock exception, req: {}, error: {}", model, e);
                throw new LockException("获取锁异常");
            }
            if (lock != null) {
                //判断记录是否已存在
                Integer count = refundMapper.queryCount(model);
                if (count == null || count < 1) {
                    refundMapper.addRefund(model);
                } else {
                    String msg = "appId[" + model.getAppId() + "]，已存在退款订单[" + model.getAppRefundId() + "]";
                    throw new IdempotencyException(msg);
                }
            } else {
                throw new LockException("锁等待超时");
            }
        } finally {
            try {
                locker.unlock(lock);
            } catch (Exception e) {
                log.error("Lock release exception: {}", e);
                throw new LockException("释放锁异常");
            }
        }
    }

    private void addTrans(Trans model, DistributedLock locker, String lockKey, TransMapper transMapper) {
        if (locker == null) {
            throw new LockException("DistributedLock is null");
        }
        if (StrUtil.isEmpty(lockKey)) {
            throw new LockException("lockKey is null");
        }
        Object lock = null;
        try {
            //加锁
            try {
                lock = locker.tryLock(lockKey, 10, 60, TimeUnit.SECONDS);
            } catch (Exception e) {
                log.error("get lock exception, req: {}, error: {}", model, e);
                throw new LockException("获取锁异常");
            }
            if (lock != null) {
                //判断记录是否已存在
                Integer count = transMapper.queryCount(model);
                if (count == null || count < 1) {
                    transMapper.addTrans(model);
                } else {
                    String msg = "appId[" + model.getAppId() + "]，已存在企业付款到零钱订单[" + model.getAppTransId() + "]";
                    throw new IdempotencyException(msg);
                }
            } else {
                throw new LockException("锁等待超时");
            }
        } finally {
            try {
                locker.unlock(lock);
            } catch (Exception e) {
                log.error("Lock release exception: {}", e);
                throw new LockException("释放锁异常");
            }
        }
    }

    private void addContract(Contract model, DistributedLock locker, String lockKey, ContractMapper contractMapper) {
        if (locker == null) {
            throw new LockException("DistributedLock is null");
        }
        if (StrUtil.isEmpty(lockKey)) {
            throw new LockException("lockKey is null");
        }
        Object lock = null;
        try {
            //加锁
            try {
                lock = locker.tryLock(lockKey, 10, 60, TimeUnit.SECONDS);
            } catch (Exception e) {
                log.error("get lock exception, req: {}, error: {}", model, e);
                throw new LockException("获取锁异常");
            }
            if (lock != null) {
                //判断记录是否已存在
                Integer count = contractMapper.queryCount(model);
                if (count == null || count < 1) {
                    contractMapper.addContract(model);
                } else {
                    String msg = "已存在签约订单[" + model.getContractCode() + "]";
                    throw new IdempotencyException(msg);
                }
            } else {
                throw new LockException("锁等待超时");
            }
        } finally {
            try {
                locker.unlock(lock);
            } catch (Exception e) {
                log.error("Lock release exception: {}", e);
                throw new LockException("释放锁异常");
            }
        }
    }

    @Override
    public void addTrade(PayReq req) {
        String lockKey = PAY + ":" + req.getAppId() + ":" + req.getAppOrderId();
        Trade model = new Trade();
        BeanUtil.copyProperties(req, model);
        model.setOrderStatus(BizConstant.WAIT_PAY);
        addTrade(model, locker, lockKey, tradeMapper);
    }

    @Override
    public void addRefund(RefundReq req) {
        String lockKey = REFUND + ":" + req.getAppId() + ":" + req.getAppRefundId();
        Refund model = new Refund();
        BeanUtil.copyProperties(req, model);
        model.setRefundStatus(BizConstant.WAIT_REFUND);
        model.setRefundType(BizConstant.REFUND_TYPE_BIZ);
        model.setRefundWay(BizConstant.REFUND_WAY_AUTO);
        addRefund(model, locker, lockKey, refundMapper);
    }

    @Override
    public void addTrans(Trans model) {
        String lockKey = TRANS + ":" + model.getAppId() + ":" + model.getAppTransId();
        addTrans(model, locker, lockKey, transMapper);
    }

    @Override
    public void addContract(Contract model) {
        String lockKey = CONTRACT + ":" + model.getContractCode();
        addContract(model, locker, lockKey, contractMapper);
    }
}
